/*********************************************************************************
 *      Copyright:  (C) 2025 TBW
 *                  All rights reserved.
 *
 *       Filename:  getaddrinfo.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(15/05/25)
 *         Author:  Tao bowen <1368725170@qq.com>
 *      ChangeLog:  1, Release initial version on "15/05/25 14:00:01"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>

int main(int argc, char *argv[]) {
    // 检查命令行参数
    if (argc != 3) {
        fprintf(stderr, "用法: %s <主机名> <服务名/端口号>\n", argv[0]);
        return 1;
    }

    const char *hostname = argv[1];  // 目标主机名
    const char *service = argv[2];   // 服务名或端口号

    struct addrinfo hints, *result, *rp;
    int sfd, s;

    // 初始化hints结构体，指定地址信息要求
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;      // 不指定IPv4或IPv6，自动选择
    hints.ai_socktype = SOCK_STREAM;  // TCP套接字
    hints.ai_flags = 0;
    hints.ai_protocol = 0;            // 任何协议

    // 调用getaddrinfo获取地址信息
    s = getaddrinfo(hostname, service, &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        return 1;
    }

    // 遍历地址信息链表并输出
    printf("为 %s:%s 获取到的地址信息:\n", hostname, service);
    for (rp = result; rp != NULL; rp = rp->ai_next) {
        void *addr;
        char ipstr[INET6_ADDRSTRLEN];

        // 获取IP地址
        if (rp->ai_family == AF_INET) {  // IPv4
            struct sockaddr_in *ipv4 = (struct sockaddr_in *)rp->ai_addr;
            addr = &(ipv4->sin_addr);
        } else {  // IPv6
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)rp->ai_addr;
            addr = &(ipv6->sin6_addr);
        }

        // 将二进制IP地址转换为文本形式
        inet_ntop(rp->ai_family, addr, ipstr, sizeof(ipstr));

        // 输出地址信息
        printf("\n地址项:\n");
        printf("  地址族: %s\n", (rp->ai_family == AF_INET) ? "IPv4" : "IPv6");
        printf("  套接字类型: %s\n", 
               (rp->ai_socktype == SOCK_STREAM) ? "TCP" : "UDP");
        printf("  IP地址: %s\n", ipstr);
        printf("  端口号: %d\n", 
               ntohs(((struct sockaddr_in *)rp->ai_addr)->sin_port));

        // 尝试使用此地址信息创建套接字并连接
        sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if (sfd == -1) {
            perror("socket");
            continue;
        }

        if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) {
            printf("  连接成功!\n");
            close(sfd);
            break;  // 找到可用地址后退出循环
        }

        close(sfd);
        printf("  连接失败\n");
    }

    // 释放地址信息链表
    freeaddrinfo(result);

    if (rp == NULL) {
        fprintf(stderr, "无法连接到任何地址\n");
        return 1;
    }

    return 0;
}
